package com.water.devtool.service;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.water.devtool.BeanUtil;
import com.water.devtool.Context;
import com.water.devtool.bean.ClassDefine;
import com.water.devtool.define.ClassBean;
import com.water.devtool.define.EntityBean;
import com.water.devtool.define.LevelConfig;
import com.water.devtool.define.ModularBean;
import com.water.devtool.define.ProjectBean;
import com.water.freemarker.FreeMarkerUtil;
import com.water.util.ConfUtil;
import com.water.util.StringGenerater;
import com.watermud.util.EasyMap;
import com.watermud.util.StringUtil;

import freemarker.template.TemplateException;

public class CodeGenerateService {
	final static Logger log = LoggerFactory.getLogger(CodeGenerateService.class);

	interface GenerateVisitor {
		public OutputStream getOutputStream(Context context) throws IOException;

		public void close();

		public String getFileName(Context context);
	}

	public Map<String, String> generateForMap(EntityBean entity) throws Exception {
		Map<String, ClassBean> classConfig = entity.getClassConfig();
		if (classConfig == null || classConfig.size() == 0) {
			log.debug("不需要生成类");
			return null;
		}
		Map<String, String> map = new HashMap<String, String>();
		Context context = new Context();
		context.setEntityBean(entity);

		ProjectBean project = entity.getProject();

		List<LevelConfig> levelConfigList = project.listLevelConfig();
		for (LevelConfig levelConfig : levelConfigList) {
			GenerateVisitor visitor = new GenerateVisitor() {
				ByteArrayOutputStream baos = new ByteArrayOutputStream();

				@Override
				public OutputStream getOutputStream(Context context) throws IOException {
					return baos;
				}

				@Override
				public void close() {
				}

				@Override
				public String getFileName(Context context) {
					return null;
				}
			};
			generateClass(context, entity, levelConfig.getLevel(), visitor);
			ByteArrayOutputStream baos = (ByteArrayOutputStream) visitor.getOutputStream(context);
			try {
				map.put(levelConfig.getLevel(), baos.toString("utf-8"));
			} catch (UnsupportedEncodingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return map;
	}

	public void generate(EntityBean entity) throws Exception {
		Map<String, ClassBean> classConfig = entity.getClassConfig();
		if (classConfig == null || classConfig.size() == 0) {
			log.debug("不需要生成类");
			return;
		}

		Context context = new Context();
		context.setEntityBean(entity);

		ProjectBean project = entity.getProject();

		List<LevelConfig> levelConfigList = project.listLevelConfig();
		for (LevelConfig levelConfig : levelConfigList) {
			generateClass(context, entity, levelConfig.getLevel(), new GenerateVisitor() {
				OutputStream os = null;

				@Override
				public OutputStream getOutputStream(Context context) throws IOException {
					String classFilePath = getFileName(context);
					os = checkFileOutputStream(classFilePath);
					return os;
				}

				@Override
				public void close() {
					if (os != null) {
						try {
							os.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}

				@Override
				public String getFileName(Context context) {
					String fullPath = (String) context.get("fullPath");
					String fileName = null;
					if (StringUtils.isNotBlank(levelConfig.getFileName())) {
						fileName = StringGenerater.get(levelConfig.getFileName(), context);
						fileName = StringGenerater.get(fileName, context);
						fileName = fileName.substring(0, 1).toLowerCase() + fileName.substring(1);
					} else {
						fileName = (String) context.get("className");
					}
					String format = (String) context.get("fileFormat");
					if (!fullPath.endsWith("/")) {
						fullPath = fullPath + "/";
					}
					log.debug("文件路径是：{}", fullPath);
					File dir = new File(fullPath);
					if (!dir.exists()) {
						dir.mkdirs();
					}
					String classFilePath = fullPath + fileName + "." + format;
					return classFilePath;
				}

			});
		}
	}

	private void generateClass(Context context, EntityBean entity, String key, GenerateVisitor visitor)
			throws Exception {
		log.info("生成实体[{}]的[{}] 类", entity.getCode(), key);
		context.put("classLevel", key);
		ModularBean mod = entity.getModular();
		ProjectBean project = mod.getProject();

		LevelConfig levelConfig = project.getLevelConfig(key);
		context.put("lastPack", levelConfig.getLastPack());
		String packageName = null;
		packageName = StringGenerater.get(project.getPackageNameStyle(), context);
		log.debug("包名是：{}", packageName);
		String className = null;
		context.put("classLevel", levelConfig.getName());
		context.put("levelName", levelConfig.getName());
		String nameStyle = levelConfig.getFileName();
		if (StringUtils.isBlank(nameStyle)) {
			nameStyle = project.getClassNameStyle();
		}
		className = StringGenerater.get(nameStyle, context);
		log.info("类名是：{}", className);
		context.put("packagePath", packageName.replace(".", "/"));
		context.put("modularPath", entity.getModular().getCode());
		String root = StringUtil.getPathStr(project.getProjectPath())
				+ (levelConfig.getRootPath() != null ? levelConfig.getRootPath() : project.getSrcRootPath());
		root = StringUtil.getPathStr(root);
		log.debug("文件根目录是：{}", root);
		String relatePath = StringGenerater.get(levelConfig.getPathStyle(), context);
		log.debug("文件相对路径是：{}", relatePath);

		ClassDefine clazz = new ClassDefine();
		clazz.setLevel(key);
		clazz.setPackageName(packageName);
		clazz.setClassName(className);
		clazz.setModelName(mod.getCode());
		clazz.setSeqName(BeanUtil.getSeqName(entity));
		context.addClassDefine(clazz);
		context.put(key, clazz);
		context.put("fullPath", root + relatePath);
		context.put("className", clazz.getClassName());
		context.put("fileFormat", levelConfig.getFileFormat());

		if (entity.getClassConfig().containsKey(key)) {
			OutputStream os = visitor.getOutputStream(context);
			Map data = EasyMap.create().put("e", entity).put("context", context).put(key, clazz)
					.put("listx", entity.getColumns()).put("author", ConfUtil.getString("author", "author")).map();
			try {
				FreeMarkerUtil.print(os, "" + levelConfig.getTemplateFile() + ".ftl", data);
				log.debug("文件生成完成：{}", className + "." + levelConfig.getFileFormat());
			} catch (IOException e1) {
				throw new Exception("文件错误：" + visitor.getFileName(context), e1);
			} catch (TemplateException e1) {
				throw new Exception("模板文件存在问题：" + key, e1);
			} finally {
				visitor.close();
			}

		}
	}

	private FileOutputStream checkFileOutputStream(String classFilePath) throws IOException {

		File classFile = new File(classFilePath);
		if (classFile.exists()) {
			// log.warn("类文件已经存在，备份之后重新生成");
			classFile.delete();
			// File target = new File(classFilePath + "." +
			// System.currentTimeMillis() + ".bak");
			// classFile.renameTo(target);
		}
		FileOutputStream os;
		try {
			os = new FileOutputStream(classFile);
		} catch (IOException e) {
			log.error("无法创建文件，检查权限" + classFilePath, e);
			throw e;
		}
		return os;
	}

	private OutputStreamWriter checkFile(String fullPath, String beanName, String format) {
		if (!fullPath.endsWith("/")) {
			fullPath = fullPath + "/";
		}
		log.debug("文件路径是：{}", fullPath);
		File dir = new File(fullPath);
		if (!dir.exists()) {
			dir.mkdirs();
		}
		String classFilePath = fullPath + beanName + "." + format;
		File classFile = new File(classFilePath);
		if (classFile.exists()) {
			// log.warn("类文件已经存在，备份之后重新生成");
			classFile.delete();
			// File target = new File(classFilePath + "." +
			// System.currentTimeMillis() + ".bak");
			// classFile.renameTo(target);
		}
		OutputStreamWriter writer;
		try {
			writer = new OutputStreamWriter(new FileOutputStream(classFile), "UTF-8");
		} catch (IOException e) {
			e.printStackTrace();
			log.error("无法创建文件，检查权限" + fullPath + beanName, e);
			return null;
		}
		return writer;
	}

}
